package org.kabeja.dxf.helpers;

import org.kabeja.dxf.entity.Bounds;
import org.kabeja.dxf.entity.DXFHatch;
import org.kabeja.math.MathUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author shutao
 * @since 2022/4/25 10:33
 */
public class HatchLineIterator implements Iterator {
    public static final double LIMIT = 1.0E-5D;
    protected double angle;
    protected Bounds hatchBounds;
    protected HatchLineFamily pattern;
    protected double length;
    protected Vector v;
    protected Vector r;
    protected List<ParametricLine> bounderyEdges;
    protected ParametricLine patternLine;
    protected double tmin = 1.0D / 0.0;
    protected double tmax = -1.0D / 0.0;
    protected double walkingLength;
    protected double currentWalkingStep = 0.0D;

    public HatchLineIterator(DXFHatch var1, HatchLineFamily var2) {
        this.angle = Math.toRadians(var2.getRotationAngle());
        this.hatchBounds = var1.getBounds();
        this.length = var2.getLength();
        this.bounderyEdges = new ArrayList<>();
        Point var3 = new Point(this.hatchBounds.getMinimumX(), this.hatchBounds.getMaximumY(), 0.0D);
        Point var4 = new Point(this.hatchBounds.getMinimumX(), this.hatchBounds.getMinimumY(), 0.0D);
        this.bounderyEdges.add(new ParametricLine(var3, MathUtils.getVector(var3, var4)));
        var3 = new Point(this.hatchBounds.getMinimumX(), this.hatchBounds.getMinimumY(), 0.0D);
        var4 = new Point(this.hatchBounds.getMaximumX(), this.hatchBounds.getMinimumY(), 0.0D);
        this.bounderyEdges.add(new ParametricLine(var3, MathUtils.getVector(var3, var4)));
        var3 = new Point(this.hatchBounds.getMaximumX(), this.hatchBounds.getMinimumY(), 0.0D);
        var4 = new Point(this.hatchBounds.getMaximumX(), this.hatchBounds.getMaximumY(), 0.0D);
        this.bounderyEdges.add(new ParametricLine(var3, MathUtils.getVector(var3, var4)));
        var3 = new Point(this.hatchBounds.getMaximumX(), this.hatchBounds.getMaximumY(), 0.0D);
        var4 = new Point(this.hatchBounds.getMinimumX(), this.hatchBounds.getMaximumY(), 0.0D);
        this.bounderyEdges.add(new ParametricLine(var3, MathUtils.getVector(var3, var4)));
        this.pattern = var2;
        this.initialize();
    }

    @Override
    public boolean hasNext() {
        return this.currentWalkingStep <= this.walkingLength;
    }

    protected void initialize() {
        if (this.length == 0.0D) {
            this.length = 1.0D;
        }

        Point var1 = new Point();
        var1.setX(this.hatchBounds.getMinimumX() + this.hatchBounds.getWidth() / 2.0D);
        var1.setY(this.hatchBounds.getMinimumY() + this.hatchBounds.getHeight() / 2.0D);
        var1.setZ(0.0D);
        this.r = new Vector();
        if (Math.abs(this.pattern.getOffsetY()) < 1.0E-5D) {
            this.r.setY(0.0D);
        } else {
            this.r.setY(this.pattern.getOffsetY());
        }

        if (Math.abs(this.pattern.getOffsetX()) < 1.0E-5D) {
            this.r.setX(0.0D);
        } else {
            this.r.setX(this.pattern.getOffsetX());
        }

        this.v = new Vector();
        this.v.setX(this.length * Math.cos(this.angle));
        this.v.setY(this.length * Math.sin(this.angle));
        if (Math.abs(this.v.getX()) < 1.0E-5D) {
            this.v.setX(0.0D);
        }

        if (Math.abs(this.v.getY()) < 1.0E-5D) {
            this.v.setY(0.0D);
        }

        double[] var2 = this.getRasterValues(var1.getX(), var1.getY());
        var1 = this.getPoint((double)Math.round(var2[0]), (double)Math.round(var2[1]));
        this.patternLine = new ParametricLine(var1, this.r);
        this.calculateIntersection(this.hatchBounds.getMinimumX(), this.hatchBounds.getMaximumY());
        this.calculateIntersection(this.hatchBounds.getMinimumX(), this.hatchBounds.getMinimumY());
        this.calculateIntersection(this.hatchBounds.getMaximumX(), this.hatchBounds.getMinimumY());
        this.calculateIntersection(this.hatchBounds.getMaximumX(), this.hatchBounds.getMaximumY());
        this.tmin = Math.floor(this.tmin);
        this.tmax = Math.ceil(this.tmax);
        Point var3 = this.patternLine.getPointAt(this.tmin);
        this.patternLine.setStartPoint(var3);
        this.walkingLength = Math.ceil(Math.abs(this.tmax - this.tmin));
    }

    protected void calculateIntersection(double var1, double var3) {
        Point var5 = new Point(var1, var3, 0.0D);
        ParametricLine var6 = new ParametricLine(var5, this.v);
        double var7 = this.patternLine.getIntersectionParameter(var6);
        if (var7 < this.tmin) {
            this.tmin = var7;
        }

        if (var7 > this.tmax) {
            this.tmax = var7;
        }

    }

    protected double[] getRasterValues(double var1, double var3) {
        double[] var5 = new double[2];
        if (this.r.getX() == 0.0D) {
            var5[0] = (var1 - this.pattern.getBaseX()) / this.v.getX();
            var5[1] = (var3 - this.pattern.getBaseY() - this.v.getY() * var5[0]) / this.r.getY();
        } else if (this.r.getY() == 0.0D) {
            var5[0] = (var3 - this.pattern.getBaseY()) / this.v.getY();
            var5[1] = (var1 - this.pattern.getBaseX()) / this.r.getX();
        } else if (this.v.getX() == 0.0D) {
            var5[1] = (var1 - this.pattern.getBaseX()) / this.r.getX();
            var5[0] = (var3 - this.pattern.getBaseY() - this.r.getY() * var5[1]) / this.v.getY();
        } else if (this.v.getY() == 0.0D) {
            var5[1] = (var3 - this.pattern.getBaseY()) / this.r.getY();
            var5[0] = (var1 - this.pattern.getBaseX() - this.r.getX() * var5[1]) / this.v.getX();
        } else {
            double var6 = this.r.getY() / this.r.getX();
            var5[0] = (var3 - this.pattern.getBaseY() - var1 * var6 + this.pattern.getBaseX() * var6) / (this.v.getY() - var6 * this.v.getX());
            var5[1] = (var1 - this.pattern.getBaseX() - this.v.getX() * var5[0]) / this.r.getX();
        }

        return var5;
    }

    @Override
    public Object next() {
        Point var1 = this.patternLine.getPointAt(this.currentWalkingStep);
        ParametricLine var2 = new ParametricLine(var1, this.v);
        Iterator<ParametricLine> var3 = this.bounderyEdges.iterator();
        List<Point> var4 = new ArrayList<>();

        while(var3.hasNext()) {
            ParametricLine var5 = var3.next();
            double var6 = var5.getIntersectionParameter(var2);
            if (var6 >= 0.0D && var6 < 1.0D) {
                var4.add(var5.getPointAt(var6));
            }
        }

        double var15 = 0.0D;
        double var7 = 0.0D;
        if (var4.size() == 2) {
            Point var9 = var4.get(0);
            double var10 = var2.getParameter(var9);
            Point var12 = var4.get(1);
            double var13 = var2.getParameter(var12);
            if (var10 > var13) {
                var2.setStartPoint(var12);
                var15 = Math.abs(var13 - Math.floor(var13)) * this.length;
            } else {
                var2.setStartPoint(var9);
                var15 = Math.abs(var10 - Math.floor(var10)) * this.length;
            }

            var7 = Math.abs(var13 - var10) * this.length;
        }

        var2.setDirectionVector(MathUtils.normalize(this.v));
        HatchLineSegment var16 = new HatchLineSegment(var2, var7, var15, this.pattern.getPattern());
        ++this.currentWalkingStep;
        return var16;
    }

    @Override
    public void remove() {
    }

    protected Point getPoint(double var1, double var3) {
        Point var5 = new Point();
        var5.setX(var3 * this.r.getX() + this.pattern.getBaseX() + this.v.getX() * var1);
        var5.setY(var3 * this.r.getY() + this.pattern.getBaseY() + this.v.getY() * var1);
        return var5;
    }
}
